/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_framework.h"
#include "ola_error.h"
#include "securec.h"
#include "ola_log.h"


static OlaFrameworkInfo g_framework;
static bool g_resetFlag = false;
static bool g_stopFlag = false;
static OlaFrameworkState g_frameworkState = OLA_FRAMEWORK_STATE_NOT_RUNING;
static OlaFrameworkHandleCallback g_stopCb = NULL;
static OlaFrameworkHandleCallback g_resetCb = NULL;

OlaFrameworkState GetFrameworkState(void)
{
    return g_frameworkState;
}

static bool IsFrameworkStop(void)
{
    return g_stopFlag;
}

static bool IsFrameworkReset(void)
{
    return g_resetFlag;
}

static void SetFrameworkState(OlaFrameworkState state)
{
    if (state == g_frameworkState) {
        return;
    }
    OlaLogNotice("framework change state[%d] to [%d]", g_frameworkState, state);
    g_frameworkState = state;
}

static void InitFrameworkLoop(void)
{
    g_resetFlag = false;
    g_stopFlag = false;
    SetFrameworkState(OLA_FRAMEWORK_STATE_INIT);
}

static int RunFrameworkBusiness(const OlaFrameworkBusiness *business, OlaFrameworkState state)
{
    if (!IsFrameworkReset() && !IsFrameworkStop()) {
        SetFrameworkState(state);
        if (business->process != NULL) {
            return business->process();
        }
    }
    return OLA_OK;
}

static void StopFrameworkBusiness(const OlaFrameworkBusiness *business)
{
    if (business->stop != NULL) {
        business->stop();
    }
    return;
}

static int FrameworkBusinessRegister(const OlaFrameworkBusiness *src,  OlaFrameworkBusiness *dst)
{
    if ((src->process != NULL) && (src->stop != NULL)) {
        dst->process = src->process;
        dst->stop = src->stop;
        return OLA_OK;
    }
    return OLA_ERROR;
}

int OlaFrameworkHookRegister(const OlaFrameworkInfo *hook)
{
    if (hook == NULL) {
        OlaLogError("hook is null");
        return OLA_ERROR;
    }
    if (g_frameworkState != OLA_FRAMEWORK_STATE_NOT_RUNING) {
        OlaLogError("framework is runing");
        return OLA_ERROR;
    }

    memset_s(&g_framework, sizeof(OlaFrameworkInfo), 0, sizeof(OlaFrameworkInfo));
    if (FrameworkBusinessRegister(&hook->provisioning, &g_framework.provisioning) != OLA_OK) {
        OlaLogInfo("netcfg business not reg");
    }

    if (FrameworkBusinessRegister(&hook->connectWifi, &g_framework.connectWifi) != OLA_OK) {
        OlaLogInfo("connect wifi business not reg");
    }

    if (FrameworkBusinessRegister(&hook->connectCloud, &g_framework.connectCloud) != OLA_OK) {
        OlaLogInfo("connect cloud business not reg");
    }

    if (FrameworkBusinessRegister(&hook->m2mRuning, &g_framework.m2mRuning) != OLA_OK) {
        OlaLogInfo("m2m runing business not reg");
    }
    g_framework.resident = hook->resident;
    return OLA_OK;
}

int OlaFrameworkHookUnregister(void)
{
    if (g_frameworkState != OLA_FRAMEWORK_STATE_NOT_RUNING) {
        OlaLogError("framework is runing");
        return OLA_ERROR;
    }
    (void)memset_s(&g_framework, sizeof(OlaFrameworkInfo), 0, sizeof(OlaFrameworkInfo));
    return OLA_OK;
}

static void FrameworkResetNotice(void)
{
    if (!IsFrameworkReset()) {
        return;
    }
    if (g_resetCb != NULL) {
        g_resetCb(OLA_HANDLE_STATE_OK);
    }
    g_resetCb = NULL;
    OlaLogNotice("framework reset");
}

static void FrameworkStopNotice(void)
{
    if (g_stopCb != NULL) {
        g_stopCb(OLA_HANDLE_STATE_OK);
    }
    g_stopCb = NULL;
    OlaLogNotice("framework exit");
}

void OlaFrameworkMain(void)
{
    do {
        FrameworkResetNotice();
        InitFrameworkLoop();

        /* 1. 配网 */
        if (RunFrameworkBusiness(&g_framework.provisioning, OLA_FRAMEWORK_STATE_NETCFG) != OLA_OK) {
            OlaLogCrit("netcfg error");
            continue;
        }

        /* 2. 等待网络连接 */
        if (RunFrameworkBusiness(&g_framework.connectWifi, OLA_FRAMEWORK_STATE_CONNECT_WIFI) != OLA_OK) {
            OlaLogCrit("network connect error");
            continue;
        }

        /* 3. 连云登录注册 */
        if (RunFrameworkBusiness(&g_framework.connectCloud, OLA_FRAMEWORK_STATE_CONNECT_CLOUD) != OLA_OK) {
            OlaLogCrit("connect to cloud error");
            continue;
        }

        /* 4. 运行态 */
        if (RunFrameworkBusiness(&g_framework.m2mRuning, OLA_FRAMEWORK_STATE_M2M_RUNING) != OLA_OK) {
            OlaLogCrit("m2m runing error");
            continue;
        }
    } while (!IsFrameworkStop() && g_framework.resident);

    SetFrameworkState(OLA_FRAMEWORK_STATE_NOT_RUNING);
    FrameworkStopNotice();
    return;
}

static void StopFrameworkCurBusiness(void)
{
    switch (g_frameworkState) {
        case OLA_FRAMEWORK_STATE_NETCFG:
            StopFrameworkBusiness(&g_framework.provisioning);
            break;
        case OLA_FRAMEWORK_STATE_CONNECT_WIFI:
            StopFrameworkBusiness(&g_framework.connectWifi);
            break;
        case OLA_FRAMEWORK_STATE_CONNECT_CLOUD:
            StopFrameworkBusiness(&g_framework.connectCloud);
            break;
        case OLA_FRAMEWORK_STATE_M2M_RUNING:
            StopFrameworkBusiness(&g_framework.m2mRuning);
            break;
        default:
            break;
    }
}

void OlaFrameworkReset(OlaFrameworkHandleCallback cb)
{
    if (cb != NULL) {
        g_resetCb = cb;
    }
    g_resetFlag = true;
    StopFrameworkCurBusiness();
}

void OlaFrameworkStop(OlaFrameworkHandleCallback cb)
{
    if (cb != NULL) {
        g_stopCb = cb;
    }
    g_stopFlag = true;
    StopFrameworkCurBusiness();
}